﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;


using Modelo_ETdA.Negocio.Analise.Formato;
using Modelo_ETdA.Negocio.Analise.Avaliacao;

namespace Modelo_ETdA.Negocio.Analise.Relatorio
{
    public class RelatorioPrioridadesDimensao : Relatorio
    {
        private static float PERC_TOL = 0.05F;
        private static float PRIORIDADE_RELEVANTE = 0.4F;
        private static float PRIORIDADE_SIGNIFICATIVO = 0.7F;
        private static float PRIORIDADE_TRIVIAL = 1.0F;

        private List<Avaliacao.Avaliacao> avalCliente;
        private List<Avaliacao.Avaliacao> avalProfissional;
        private List<Avaliacao.Avaliacao> avalAnalista;
        private FormatoAvaliacaoCliente formAvalCliente;
        private FormatoAvaliacaoProfissional formAvalProfissional;
        private FormatoAvaliacaoAnalista formAvalAnalista;

        private FichaTecnica3D fich_3D;


        /*
         * List[0] -> Cliente
         * List[1] -> Profissional
         * List[2] -> Analista
         * 
         *  int == 0 -> Relevante
         *  int == 1 -> Significativo
         *  int == 2 -> Trivial
         * 
         */
        private Dictionary<String, List<int>> mapDescConsideracaoPrioridades;


        private Dictionary<int, DistribuicaoRespostas> mapConsDistRespCliente;
        private Dictionary<int, DistribuicaoRespostas> mapConsDistRespProfissional;
        private Dictionary<int, DistribuicaoRespostas> mapConsDistRespAnalista;

        public RelatorioPrioridadesDimensao(List<Avaliacao.Avaliacao> avaliacaoCliente, List<Avaliacao.Avaliacao> avaliacaoProfissional, List<Avaliacao.Avaliacao> avaliacaoAnalista, FormatoAvaliacaoCliente formAvalCliente, FormatoAvaliacaoProfissional formAvalProfissional, FormatoAvaliacaoAnalista formAvalAnalista, AmbienteComercial.AmbienteComercial ambC)
            : base(ambC)
        {
            this.avalCliente = avaliacaoCliente;
            this.avalProfissional = avaliacaoProfissional;
            this.avalAnalista = avaliacaoAnalista;

            this.formAvalCliente = formAvalCliente;
            this.formAvalProfissional = formAvalProfissional;
            this.formAvalAnalista = formAvalAnalista;


            mapDescConsideracaoPrioridades = new Dictionary<String, List<int>>();
            mapConsDistRespCliente = new Dictionary<int, DistribuicaoRespostas>();
            mapConsDistRespProfissional = new Dictionary<int, DistribuicaoRespostas>();
            mapConsDistRespAnalista = new Dictionary<int, DistribuicaoRespostas>();
        }

        private void createMapDistResp(List<Avaliacao.Avaliacao> l_aval, Dictionary<int, DistribuicaoRespostas> map, FormatoAvaliacao form, Boolean e_cliente, List<Consideracao> l_cons)
        {
            int est_cod_cons;
            Questao est_quest;
            Avaliacao.Questionario clt_quest;

            foreach (Consideracao cons in l_cons) map.Add(cons.Cod, new DistribuicaoRespostas(0, 0, 0, cons.Cod));


            foreach (Avaliacao.Avaliacao aval in l_aval)
            {
                foreach (Avaliacao.Resposta resp in aval.Respostas)
                {
                    est_quest = form.getQuestao(resp.CodQuestao);
                    est_cod_cons = est_quest.CodConsideracao;
                    if (!map.ContainsKey(est_cod_cons)) map.Add(est_cod_cons, new DistribuicaoRespostas(0, 0, 0, est_cod_cons));
                    if (resp.ValorResposta > 0)
                    {
                        switch (getSatisf(resp.ValorResposta, est_quest.EscalaMin, est_quest.EscalaMax, est_quest.MinResposta))
                        {
                            case 0:
                                map[est_cod_cons].NumRespNeg++;
                                if (est_quest.MinResposta >= resp.ValorResposta)
                                {
                                    if (e_cliente)
                                    {
                                        clt_quest = (Avaliacao.Questionario)aval;
                                        if (clt_quest.Cliente) map[est_cod_cons].NumRespCorteClt++;
                                    }
                                }
                                break;
                            case 1:
                                map[est_cod_cons].NumRespSatisf++;
                                break;
                            case 2:
                                map[est_cod_cons].NumRespPos++;
                                break;
                        }


                        if (est_quest.MinResposta >= resp.ValorResposta)
                        {
                            map[est_cod_cons].NumRespCorte++;
                        }
                    }
                }
            }

        }


        private List<float> getPrioridadeCliente()
        {
            List<float> ret = new List<float>();
            List<Avaliacao.Avaliacao> l_aval = avalCliente;
            Questionario quest;
            Dictionary<int, float> mapConsVal = new Dictionary<int, float>();
            Dictionary<int, int> mapConsNum = new Dictionary<int, int>();

            QuestaoCliente questao;

            foreach (Avaliacao.Avaliacao aval in l_aval)
            {
                quest = (Questionario)aval;
                foreach (Resposta resp in aval.Respostas)
                {
                    if (resp.ValorResposta > 0)
                    {
                        questao = (QuestaoCliente)formAvalCliente.getQuestao(resp.CodQuestao);


                        if (!mapConsVal.ContainsKey(questao.CodConsideracao)) mapConsVal.Add(questao.CodConsideracao, 0);
                        if (!mapConsNum.ContainsKey(questao.CodConsideracao)) mapConsNum.Add(questao.CodConsideracao, 0);

                        mapConsVal[questao.CodConsideracao] += (resp.ValorResposta * questao.Ponderacao) / questao.EscalaMax;
                        mapConsNum[questao.CodConsideracao] += 1;
                    }
                }
            }

            for (int i = 0; i <= 14; i++) ret.Add(-1);
            for (int i = 0; i <= 14; i++)
            {
                if (mapConsVal.ContainsKey(i))
                {
                    ret[i] = (mapConsVal[i] / (float)mapConsNum[i]);
                }
                else ret[i] = -1;
            }

            return ret;
        }

        private List<float> getPrioridadeProfissional()
        {
            List<float> ret = new List<float>();
            List<Avaliacao.Avaliacao> l_aval = avalProfissional;
            FormularioAvaliacao quest;
            Dictionary<int, float> mapConsVal = new Dictionary<int, float>();
            Dictionary<int, int> mapConsNum = new Dictionary<int, int>();

            Questao questao;

            foreach (Avaliacao.Avaliacao aval in l_aval)
            {
                quest = (FormularioAvaliacao)aval;
                foreach (Resposta resp in aval.Respostas)
                {
                    questao = (Questao)formAvalProfissional.getQuestao(resp.CodQuestao);

                    if (!mapConsVal.ContainsKey(questao.CodConsideracao)) mapConsVal.Add(questao.CodConsideracao, 0);
                    if (!mapConsNum.ContainsKey(questao.CodConsideracao)) mapConsNum.Add(questao.CodConsideracao, 0);

                    mapConsVal[questao.CodConsideracao] += (resp.ValorResposta * questao.Ponderacao) / questao.EscalaMax;
                    mapConsNum[questao.CodConsideracao] += 1;
                }
            }
            for (int i = 0; i <= 14; i++) ret.Add(-1);
            for (int i = 0; i <= 14; i++)
            {
                if (mapConsVal.ContainsKey(i))
                {
                    ret[i] = (mapConsVal[i] / (float)mapConsNum[i]);
                }
                else ret[i] = -1;
            }

            return ret;
        }



        private List<float> getPrioridadeAnalista()
        {
            List<float> ret = new List<float>();
            List<Avaliacao.Avaliacao> l_aval = avalAnalista;
            Checklist quest;
            Dictionary<int, float> mapConsVal = new Dictionary<int, float>();
            Dictionary<int, int> mapConsNum = new Dictionary<int, int>();

            QuestaoAnalista questao;

            foreach (Avaliacao.Avaliacao aval in l_aval)
            {
                quest = (Checklist)aval;
                foreach (Resposta resp in aval.Respostas)
                {
                    questao = (QuestaoAnalista)formAvalAnalista.getQuestao(resp.CodQuestao);

                    if (!mapConsVal.ContainsKey(questao.CodConsideracao)) mapConsVal.Add(questao.CodConsideracao, 0);
                    if (!mapConsNum.ContainsKey(questao.CodConsideracao)) mapConsNum.Add(questao.CodConsideracao, 0);

                    mapConsVal[questao.CodConsideracao] += (resp.ValorResposta * questao.Ponderacao) / questao.EscalaMax;
                    mapConsNum[questao.CodConsideracao] += 1;
                }
            }

            for (int i = 0; i <= 14; i++) ret.Add(-1);
            for (int i = 0; i <= 14; i++)
            {
                if (mapConsVal.ContainsKey(i))
                {
                    ret[i] = (mapConsVal[i] / (float)mapConsNum[i]);
                }
                else ret[i] = -1;
            }

            return ret;
        }


        private int converteValCor(float val)
        {
            if (val <= 0) return -1;
            if (val <= PRIORIDADE_RELEVANTE) return 0;
            if (val <= PRIORIDADE_SIGNIFICATIVO) return 1;
            return 2;
        }

        /*
         * 0 -> Negativo
         * 1 -> Satisfatorio
         * 2 -> Positivo
         */
        private int getSatisf(int val_resp, int escalaMin, int escalaMax, int minResp)
        {
            if (val_resp < minResp) return 0;
            if ((val_resp) <= (escalaMax * 0.4)) return 0;
            if ((val_resp) <= (escalaMax * 0.7)) return 1;

            return 2;
        }


        private void calcular()
        {
            Dados.DadosAnalises da = new Dados.DadosAnalises();
            List<Consideracao> l_cons = da.getListConsideracao();

            createMapDistResp(avalAnalista, mapConsDistRespAnalista, formAvalAnalista, false, l_cons);
            createMapDistResp(avalProfissional, mapConsDistRespProfissional, formAvalProfissional, false, l_cons);
            createMapDistResp(avalCliente, mapConsDistRespCliente, formAvalCliente, true, l_cons);


            DistribuicaoRespostas dist_resp_clt;
            DistribuicaoRespostas dist_resp_pro;
            DistribuicaoRespostas dist_resp_anal;

            int resp_corte;
            int resp_corte_clt;
            int resp_corte_nclt;

            float pond_resp_corte;
            float pond_tol_corte;

            Boolean corteClt;
            Boolean cortePro;
            Boolean corteAnal;

            List<float> val_pri_clt = getPrioridadeCliente();
            List<float> val_pri_pro = getPrioridadeProfissional();
            List<float> val_pri_anal = getPrioridadeAnalista();

            Checklist av_anal = (Checklist)avalAnalista[0];

            List<int> aux_init;

            foreach (Consideracao cons in l_cons)
            { //Verifica se esta consideração é cortada em qualquer uma das dimensões


                //Inicializacao do map
                aux_init = new List<int>();
                aux_init.Add(0); aux_init.Add(0); aux_init.Add(0);
                mapDescConsideracaoPrioridades.Add(cons.Descricao, aux_init);



                corteClt = false;
                cortePro = false;
                corteAnal = false;

                dist_resp_clt = mapConsDistRespCliente[cons.Cod];
                dist_resp_pro = mapConsDistRespProfissional[cons.Cod];
                dist_resp_anal = mapConsDistRespAnalista[cons.Cod];

                resp_corte = dist_resp_clt.NumRespCorte;
                resp_corte_clt = dist_resp_clt.NumRespCorteClt;
                resp_corte_nclt = resp_corte - resp_corte_clt;

                pond_resp_corte = ((float)(((float)formAvalCliente.ValorCliente / 100) + 1) * resp_corte_clt) + resp_corte_nclt;
                pond_tol_corte = (float)(dist_resp_clt.NumRespNeg + dist_resp_clt.NumRespPos + dist_resp_clt.NumRespSatisf) * PERC_TOL;

                if (pond_resp_corte >= pond_tol_corte) { corteClt = true; mapDescConsideracaoPrioridades[cons.Descricao][0] = 0; }


                pond_resp_corte = dist_resp_pro.NumRespCorte;
                pond_tol_corte = (float)(dist_resp_pro.NumRespNeg + dist_resp_pro.NumRespPos + dist_resp_pro.NumRespSatisf) * PERC_TOL;

                if (pond_resp_corte >= pond_tol_corte) { cortePro = true; mapDescConsideracaoPrioridades[cons.Descricao][1] = 0; }

                pond_resp_corte = dist_resp_anal.NumRespCorte;
                pond_tol_corte = (float)(dist_resp_anal.NumRespNeg + dist_resp_anal.NumRespPos + dist_resp_anal.NumRespSatisf) * PERC_TOL;

                if (pond_resp_corte >= pond_tol_corte) { corteAnal = true; mapDescConsideracaoPrioridades[cons.Descricao][2] = 0; }

                if ((cons.Descricao.CompareTo("Ruido") == 0))
                {
                    if ((av_anal.ValRuido > formAvalAnalista.MaxRuido) || (av_anal.ValRuido < formAvalAnalista.MinRuido)) { corteAnal = true; mapDescConsideracaoPrioridades[cons.Descricao][2] = 0; }
                }

                if ((cons.Descricao.CompareTo("Temperatura") == 0))
                {
                    if ((av_anal.ValTemp > formAvalAnalista.MaxTemp) || (av_anal.ValTemp < formAvalAnalista.MinTemp)) { corteAnal = true; mapDescConsideracaoPrioridades[cons.Descricao][2] = 0; }
                }

                if ((cons.Descricao.CompareTo("Iluminação") == 0))
                {
                    if ((av_anal.ValLum > formAvalAnalista.MaxLum) || (av_anal.ValLum < formAvalAnalista.MinLum)) { corteAnal = true; mapDescConsideracaoPrioridades[cons.Descricao][2] = 0; }
                }

                if (converteValCor(val_pri_clt[cons.Cod]) == -1) mapDescConsideracaoPrioridades[cons.Descricao][0] = converteValCor(val_pri_clt[cons.Cod]);

                if (!corteClt) mapDescConsideracaoPrioridades[cons.Descricao][0] = converteValCor(val_pri_clt[cons.Cod]);

                if (!cortePro) mapDescConsideracaoPrioridades[cons.Descricao][1] = converteValCor(val_pri_pro[cons.Cod]);

                if (!corteAnal) mapDescConsideracaoPrioridades[cons.Descricao][2] = converteValCor(val_pri_anal[cons.Cod]);

            }

            this.fich_3D = new FichaTecnica3D(avalCliente.Count, avalCliente[0].Doc_cabecalho.Data_registo, avalProfissional.Count, avalProfissional[0].Doc_cabecalho.Data_registo, avalAnalista.Count, avalAnalista[0].Doc_cabecalho.Data_registo);
        }


        public override int imprimir()
        {
            calcular();
            Dados.DadosRelatorio da = new Dados.DadosRelatorio();
            return da.imprimirPrioridadeDimensao(this);
        }

        public List<Avaliacao.Avaliacao> AvalCliente
        {
            get { return avalCliente; }
            set { avalCliente = value; }
        }

        public List<Avaliacao.Avaliacao> AvalProfissional
        {
            get { return avalProfissional; }
            set { avalProfissional = value; }
        }

        public List<Avaliacao.Avaliacao> AvalAnalista
        {
            get { return avalAnalista; }
            set { avalAnalista = value; }
        }

        public FormatoAvaliacaoCliente FormAvalCliente
        {
            get { return formAvalCliente; }
            set { formAvalCliente = value; }
        }

        public FormatoAvaliacaoProfissional FormAvalProfissional
        {
            get { return formAvalProfissional; }
            set { formAvalProfissional = value; }
        }

        public FormatoAvaliacaoAnalista FormAvalAnalista
        {
            get { return formAvalAnalista; }
            set { formAvalAnalista = value; }
        }

        public FichaTecnica3D Fich_3D
        {
            get { return fich_3D; }
            set { fich_3D = value; }
        }

        public Dictionary<String, List<int>> MapDescConsideracaoPrioridades
        {
            get { return mapDescConsideracaoPrioridades; }
            set { mapDescConsideracaoPrioridades = value; }
        }
    }
}
